package com.vadmin.vadminjava.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ijpay.wxpay.WxPayApi;
import com.vadmin.vadminjava.aes.AesUtil;
import com.vadmin.vadminjava.common.*;
import com.vadmin.vadminjava.db.DBUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;

import static com.ijpay.core.enums.RequestMethod.POST;
import static com.ijpay.wxpay.enums.WxApiType.CREATE_BUSINESS_COUPON;
import static com.ijpay.wxpay.enums.WxApiType.USE_BUSINESS_COUPON;
import static com.ijpay.wxpay.enums.WxDomain.CHINA;
import static com.vadmin.vadminjava.common.WxCouponUtils.*;


/* ***********************************************************
 *
 * 微信的卡劵接口： 这是商家券的主要逻辑，与微信服务交互的
 *
 * **********************************************************/
@Component
public class WxCouponBusinessService extends CommonExtends {

	org.slf4j.Logger logger = LoggerFactory.getLogger(WxCouponCashService.class.getName());

	@Autowired
	AdminLogService adminLogService;
	@Autowired
	WxCouponUtils   wxCouponUtils;
	@Autowired
	WxUserMoneyService wxUserMoneyService;


	/**
	 * 请求微信创建券
	 * 2022-03-13 测试通过
	 * @param params
	 * @return
	 */
	public JSONObject createV3Coupon(JSONObject params) {
		logger.info("创建卡券函数>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
		DBUtil dbUtil = new DBUtil();
		JSONObject map = new JSONObject();
		map.put("resultFlag", false);
		map.put("resultMsg", "创建卡券失败");

		//参数处理
		String stockName = params.getString("stockName");
		String couponAmount = params.getString("couponAmount");
		String transactionMinimum = params.getString("transactionMinimum");
		String maxCoupons = params.getString("maxCoupons");
		String maxCouponsPerUser = params.getString("maxCouponsPerUser");
		String merchantName = params.getString("merchantName");
		String couponCodeMode = params.getString("couponCodeMode");//WECHATPAY_MODE
		String useTimeStart = params.getString("useTimeStart");//
		String useTimeEnd = params.getString("useTimeEnd");//

		//限制
		if(StringUtils.isEmpty(stockName)){ map.put("resultMsg", "提交失败，缺少卡券标题参数：stockName");return map; }
		if(StringUtils.isEmpty(couponAmount)){ map.put("resultMsg", "提交失败，缺少卡券面额参数：couponAmount");return map; }
		if(StringUtils.isEmpty(transactionMinimum)){ map.put("resultMsg", "提交失败，缺少卡券使用门槛参数：transactionMinimum");return map; }
		if(StringUtils.isEmpty(maxCoupons)){ map.put("resultMsg", "提交失败，缺少卡券发行数参数：maxCoupons");return map; }
		if(StringUtils.isEmpty(maxCouponsPerUser)){ map.put("resultMsg", "提交失败，缺少单人允许领取卡券数参数：maxCouponsPerUser");return map; }
		if(StringUtils.isEmpty(merchantName)){ map.put("resultMsg", "提交失败，缺少商户名称参数：merchantName");return map; }
		if(StringUtils.isEmpty(couponCodeMode)){ map.put("resultMsg", "提交失败，缺少卡券模式参数：couponCodeMode");return map; }

		//todo 2022-04-01加入，未实际测试
		//前端输入的是元，微信接收的是分，这里要转成分
		Integer _couponAmount = zhuanFen(couponAmount);
		Integer _transactionMinimum = zhuanFen(transactionMinimum);

		//卡券数据
		JSONObject coupon = new JSONObject();
		coupon.put("stockName", stockName);//
		coupon.put("couponAmount", _couponAmount);//减多少金额，目前只用来显示
		coupon.put("transactionMinimum", _transactionMinimum);//使用门槛金额，目前只用来显示
		coupon.put("maxCoupons", maxCoupons);//发行XX张
		coupon.put("maxCouponsPerUser", maxCouponsPerUser);//单个用户最多领取取数
		coupon.put("naturalPersonLimit", false);//不知什么东西
		coupon.put("preventApiAbuse", false);//不知什么东西
		coupon.put("merchantName", merchantName);
		coupon.put("belongMerchant", MCH_ID);
		coupon.put("couponCodeMode", couponCodeMode);//券code模式为WECHATPAY_MODE时，是微信自动分配券code，商户不需要预存code；适用于多种场景
		coupon.put("beginTimestamp", useTimeStart);
		coupon.put("endTimestamp", useTimeEnd);
		coupon.put("notifyAppid", GzhAPPID);

		//组织请求的json参数字符串
		String requestJson = requestJsonGenerate(coupon);
		logger.info("创建商家券请求JSON:{}",requestJson);

		/**/
		try {
			//Map<String, Object> stringObjectMap = WxPayApi.v3Execution(POST, CHINA.toString(), CREATE_COUPON_STOCKS.toString(), MCH_ID, SERIAL_NO, KEY_PATH, requestJson);
			Map<String, Object> stringObjectMap = WxPayApi.v3Execution(POST, CHINA.toString(), CREATE_BUSINESS_COUPON.toString(), MCH_ID, SERIAL_NO, wxCouponUtils.KEY_PATH, requestJson);
			String responseBody = stringObjectMap.get("body").toString().replaceAll("[{}]","");//返回的body

			//判断是否成功
			if (Integer.parseInt(stringObjectMap.get("status") + "") == 200) {
				//取得要的数据
				String _stockId = JSONObject.parseObject(stringObjectMap.get("body") + "").getString("stock_id");
				coupon.put("stockId", _stockId);
				//写入数据库
				Integer transactionMinimum2 = coupon.getInteger("transactionMinimum") / 100;//上面转成了分，再转成元
				Integer couponAmount2       = coupon.getInteger("couponAmount") / 100;//上面转成了分，再转成元
				StringBuffer sb = new StringBuffer();
				sb.append("INSERT INTO `wx_coupon`( `merchant_name`, `stock_name`,`stock_id`, `available_begin_time`, `available_end_time`, `discount_amount`, `transaction_minimum`, `max_coupons`, `max_coupons_per_user`, `create_time`) VALUES (");
				sb.append("'" + coupon.getString("merchantName") + "', ");
				sb.append("'" + coupon.getString("stockName") + "', ");
				sb.append("'" + _stockId + "', ");
				sb.append("'" + coupon.getString("beginTimestamp") + "',");
				sb.append("'" + coupon.getString("endTimestamp") + "',");
				sb.append("'" + couponAmount2 + "',");
				sb.append("'" + transactionMinimum2 + "',");
				sb.append("'" + coupon.getString("maxCoupons") + "',");
				sb.append("'" + coupon.getString("maxCouponsPerUser") + "',");
				sb.append(" NOW())");
				String sql = sb.toString();
				dbUtil.insertOrUpdateInfoTONum(sql); // 写入表
				System.out.println(sql);
				//记录
				logger.info("创建卡券成功,stockId：{}", _stockId);
				adminLogService.write(LogEnum.COUPON_CREATE.getName(), LogEnum.COUPON_CREATE.getIndex(), "创建卡券成功！！！！数据：" + coupon.toString().replaceAll("[{}]",""));
				map.put("resultFlag", true);
				map.put("resultMsg", "创建卡券成功了");
				map.put("data", _stockId);
			} else {
				logger.error("创建卡券失败，返回信息:{}", responseBody);
				adminLogService.write(LogEnum.COUPON_CREATE.getName(), LogEnum.COUPON_CREATE.getIndex(), "创建卡券失败了。。。,返回信息：" + responseBody);
				map.put("resultFlag", false);
				map.put("resultMsg", "创建卡券失败");
				map.put("data", responseBody);
			}


		} catch (Exception ex) {
			//e.printStackTrace();
			logger.error("创建卡券处理过程出错了：{}",ex);
		} finally {
			dbUtil.closeNoErr();
		}



		return map;
	}

	/**
	 * 发放商家卷,或者说领取，其实提供一个url跳转，到微信的页面领取
	 * 领取回调：https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_15.shtml
	 * @description:
	 * @param:
	 * @return:
	 * @author
	 * @date: 2022/3/11 11:32
	 */
	public JSONObject fetchBusinessCouponUrl(JSONObject params) throws Exception {
		DBUtil dbUtil = new DBUtil();
		JSONObject map = new JSONObject();
		map.put("resultFlag",false);
		map.put("resultMsg","操作失败");

		//参数
		String openId = params.getString("openId");
		String couponId = params.getString("couponId");
		Long outRequestNo = System.currentTimeMillis() + ThreadLocalRandom.current().nextInt(10000);

		//签名数据
		Map data = new HashMap();
		data.put("open_id", openId);
		data.put("stock_id", couponId);
		data.put("out_request_no", outRequestNo);//随机单号
		data.put("send_coupon_merchant", MCH_ID);//商户id
		try {
			String key            = MCH_KEY;
			String stringSignTemp = paySignDesposit(data, key);
			logger.info("签名内容：{}", stringSignTemp);
			data.put("sign", stringSignTemp);

			map.put("data","https://action.weixin.qq.com/busifavor/getcouponinfo?" + getAsciiSort(data) + "#wechat_redirect");
			map.put("resultFlag",true);
			map.put("resultMsg","操作成功");

		} catch (Exception ex) {
			map.put("resultFlag",false);
			map.put("resultMsg","操作失败,出错了");
			logger.error("领取优惠券时出错了,openId：{}，couponId：{} , {}",openId,couponId,ex);
		} finally {
		    dbUtil.closeNoErr();
		}

		return map;
	}


	/**
	 * 核销商家卷
	 * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter9_2_3.shtml
	 */
	public Map<String, Object> useCouponBusinessToWeixinServer(JSONObject params) {
		// 面额和门槛(单位都是分)

		Map<String, Object> stringObjectMap = new HashMap<>();
		String requestJson = "{\n" +
				"  \"coupon_code\": \"" + params.getString("couponCode") + "\",\n" +
				"  \"stock_id\": \"" + params.getString("couponId") + "\",\n" +
				"  \"appid\": \"" + GzhAPPID + "\",\n" +
				"  \"use_time\": \"" + getRfc3339(Calendar.getInstance().getTime()) + "\",\n" +
				"  \"use_request_no\": \"" + params.getString("outRequestNo") + "\",\n" +
				"  \"openid\": \"" + params.getString("openId") + "\"\n" +
				"}\n";

		try {
			//首次{serialNumber=201D85E3CB2B2D9E99C937C12E736DA049209D77, body={"openid":"oqGpx6O8tX3tTAEx0SyzfTVS0d_Q","stock_id":"1210840000000013","wechatpay_use_time":"2022-04-05T15:15:44+08:00"}, signature=pVpp2uGGukw0l60y+yBSE0EPvoPgLskcFU1F4CPbzTLAKZ9GqpIctu19sYaQPlfb/nkzrVePGDXZpx1zlpFimOHvMZzF+GmHQ0iwdFuYgyaSozlo0PVOivHomj2Fx4YOajdfHqebtf3e4ZK7Q6+N2av48e64+FJnUD7DA5m/a+B1UPvVHSgbqgqRgYQTdLld3uC6akDb9s9iDelK2kzElVsfhjewpTpcL2Ab+3KqOhBM7vpdLSHvK4HD9U7NKEnNrgyGOLSPP1TJt6iweFH+095tSDvvOFEq54MTDDRmN4rmmnv7kvxpSVG6GcsX/Qk7m1AWqt5Hqm8dy/hUwr1hBg==, nonceStr=7ad2eb31fbb3da9de8837bb6cfe939a6, timestamp=1649142944, status=200}
			//重复{serialNumber=201D85E3CB2B2D9E99C937C12E736DA049209D77, body={"code":"RESOURCE_ALREADY_EXISTS","message":"券已被其他订单核销"}, signature=HGRDmL0ntntyrXG6U+1eCfr3JHgiIr8imoAAut1wdGBiV+L30atc2sJPD3p9fI8fvlzPLp+mORVXpnUfFlbQhDPcGzzDN/oS2FbucmJVxptK0KBbKOQD0HaZr+a8GBqfnYcdePbyLUTMHUek46/rEKoKMtRIHT2CVLy1CF1oZmr9nmbT/udRk+TiMPqqHBPDhBzijdInbjJiXs9iNHJr9TaoaHQr8fNQwFTJtF2PIFzggf3elVE1jMUc6syBEzIiRJIBsA3UBk5XphM4zHn1VoMNWq94G+3tii5Hu2TpMK5Ld8JL4UcAO2bg9wC7CZp/PAm69jaiet5066Kor9ykRQ==, nonceStr=f8865d54acc2a4a1185966ee480fb7ac, timestamp=1649143837, status=400}
			stringObjectMap = WxPayApi.v3Execution(POST, CHINA.toString(), USE_BUSINESS_COUPON.toString(), MCH_ID, SERIAL_NO, wxCouponUtils.KEY_PATH, requestJson);
			//logger.info("核销请求参数：{}", requestJson);
			logger.info("核销返回原始数据：{}", stringObjectMap);

		} catch (Exception e) {
			e.printStackTrace();
		}
		return stringObjectMap;

	}


	/**
	 * 商家券通知处理分发
	 * 注意，这里有分各类的，有商家券和代金券，目前项目用的是商家券注意，这里有分各类的，有商家券和代金券，目前项目用的是商家券
	 * @param params
	 * @param body
	 * @return
	 */
	public JSONObject couponNotifyDispatchOfBusiness(JSONObject params,String body){
		//初始
		//DBUtil dbUtil = new DBUtil();
		JSONObject map = new JSONObject();
		map.put("code","FAIL");
		map.put("message","结果");
		Boolean ret = false; //用于判断是否成功
		logger.info("接收到了商家券回调2>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>{}",body);

		try {
			//处理参数与数据
			JSONObject bodyJSON = JSONObject.parseObject(body);
			String eventType = bodyJSON.getString("event_type");
			JSONObject resource = JSONObject.parseObject(bodyJSON.getString("resource")); //取出resource，转为json对象
			logger.info("商家券回调数据解析成JSON：{}", bodyJSON);

			//从resource中取得加密的内容，
			//JSONObject ciphertext = JSONObject.parseObject(resource.getString("ciphertext")); //这是开发中已经转成json，所以直接取的
			String ciphertext = resource.getString("ciphertext");

			//对ciphertext内容进行解密
			AesUtil aesUtil         = new AesUtil(APIV3_KEY.getBytes());
			String  original_type   = "transaction"; // 加密前的对象类型 ，这他妈是什么东西？？？？？？？？？？？
			String  algorithm       = "AEAD_AES_256_GCM"; // 加密算法
			String  nonce           = resource.getString("nonce");
			String  associated_data = resource.getString("associated_data");

			String decryptToString = aesUtil.decryptToString(associated_data.getBytes(), nonce.getBytes(), ciphertext);
			JSONObject decryptToJSON = JSONObject.parseObject(decryptToString);

			logger.info("商家券回调数据ciphertext解密结果:{}",decryptToJSON);

			//根据事件类型分发处理
			if(eventType.equals("COUPON.SEND")){ //如果是商家券领券通知
				ret = couponSendNotifyProcess(decryptToJSON);
			}else{

			}

			//根据处理结果返回相关的数据, 这里的code和messa
			if(ret == true){
				map.put("code","SUCCESS");
				map.put("message","商家券处理成功了！");
			}else {
				map.put("code","FAIL");
				map.put("message","商家券回调详细处理不成功，可能是sql出错？？");
			}

		} catch (Exception ex) {
			map.put("code","FAIL");
			map.put("message","商家券回调分发处理出错了。。");
			logger.error("商家券回调分发处理出错了：{}",ex);
		} finally {
			//dbUtil.closeNoErr();
		}
		//返回处理
		return map;
	}


	/**
	 * 商家券通知回调的处理
	 * 2022-03-29 开发完初步测试
	 * 2022-03-31 增加数量更新操作
	 * @return
	 */
	public Boolean couponSendNotifyProcess(JSONObject notifyRes) throws SQLException {
		//初始
		DBUtil dbUtil = new DBUtil();
		Boolean ret = false;//用于返回是否成功
		Integer i = 0; // 这个用于累计sql成功执行数，既然用了事务，就不一定用到?????????????????????
		String log = ""; //日志
		logger.info("进行商家券通知回调的处理>>>>>>");
		try {
			dbUtil.conn.setAutoCommit(false);//不提交

			//参数处理
			String event_type    = notifyRes.getString("event_type");
			String open_id       = notifyRes.getString("openid");
			String unionid       = notifyRes.getString("unionid");//这个不一定会有
			String coupon_id     = notifyRes.getString("stock_id");
			String coupon_code   = notifyRes.getString("coupon_code");//这个是领取后给用户的券码
			String send_channel  = notifyRes.getString("send_channel");
			String send_merchant = notifyRes.getString("send_merchant");
			String attach_info   = notifyRes.getString("attach_info");

			// 写入客户领券明细表（券允许设置领取多张，所以只要推送，代表领取成功，一定会写入这个数字）
			String sql = "INSERT INTO wx_coupon_user(open_id, coupon_id, unionid, coupon_code, event_type, send_channel, send_merchant, attach_info, create_time) VALUES ( " +
					"'"+open_id+"', '"+coupon_id+"', '"+unionid+"', '"+coupon_code+"', '"+event_type+"', '"+send_channel+"', '"+send_merchant+"', '"+attach_info+"',NOW())";
			i = i + dbUtil.insertOrUpdateInfoTONum(sql);

			//更新数量（字段代表是已领取还是剩余，看实际情况）
			String sql2 = "update wx_coupon set fetch_coupons = fetch_coupons + 1 where stock_id = '"+coupon_id+"'";
			dbUtil.insertOrUpdateInfoTONum(sql2);

			//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 佣金处理 类型码已校验 ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

			//先查看这个openid有没有进行过领取事件的分佣
			String sqlC = "select count(id) as count1 from wxwork_user_money_log " +
					"where coupon_id='"+coupon_id+"' and open_id ='"+open_id+"' and event_code = '"+MoneyLogEnum.COUPON_FETCH.code+"'";
			Integer count = ResultSetToJavaBean.copyJsonSingleQuery(dbUtil,sqlC).getInteger("count1");

			if(count==0){//进行分佣

				//找到这个openid的外部客户的所属员工(可能有多个员工,应该是最早的那个)
				// 以关系表为准
				String sql3 = "SELECT wr.userid,wr.external_userid,wur.open_id " +
						" from wxwork_relation wr " +
						" LEFT JOIN wx_user_relation wur " +
						" on wur.external_userid = wr.external_userid " +
						" where wur.open_id = '"+open_id+"'";
				JSONObject rs = ResultSetToJavaBean.copyJsonSingleQuery(dbUtil,sql3);//得到单个记录集
				String _referrer = rs.getString("userid");
				String _externalUserid = rs.getString("externalUserid");
				String _amount = WxWorkConfig.COMMISSION_FETCH.toString();//操作资金数

				//找到员工的资金数据，进行计算
				String sql4 = "select * from wxwork_user where user_id = '" + _referrer + "'";
				Double _money = ResultSetToJavaBean.copyJsonSingleQuery(dbUtil, sql4).getDouble("money");
				String _moneyAfter = BigDecimalUtils.add(_money,_amount).toString();

				//在这个位置更新员工资金
				wxUserMoneyService.userMoneyChange(dbUtil,_referrer,_amount);//写入

				//记录明细
				JSONObject logParams = new JSONObject();
				logParams.put("user_id",_referrer);
				logParams.put("external_userid",_externalUserid);
				logParams.put("open_id",open_id);
				logParams.put("money_before",_money);
				logParams.put("money",_amount);
				logParams.put("money_after",_moneyAfter);
				logParams.put("coupon_id",coupon_id);//
				logParams.put("coupon_code",coupon_code);
				logParams.put("event",     MoneyLogEnum.COUPON_FETCH.name);
				logParams.put("event_code",MoneyLogEnum.COUPON_FETCH.code);
				logParams.put("type",      MoneyLogEnum.TYPE_ADD.name);
				logParams.put("type_code", MoneyLogEnum.TYPE_ADD.code);
				logParams.put("remark","进行了领券分佣");
				wxUserMoneyService.userMoneyLog(dbUtil,logParams);//写入

				log = "用户"+open_id+"首次领取了券"+coupon_id+"，券码是"+coupon_code+"，进行分佣处理>>";
				logger.info(log);

			}else{
				log = "用户"+open_id+"领了"+coupon_id+"的券，券码："+coupon_code+"，但已经存在这个券的分佣，不重复分佣。";
				logger.info(log);
			}


			dbUtil.conn.commit();//提交
			ret = true;
			log = "商家券通知回调的领券处理结束。";

		} catch (Exception ex) {
			log = "商家券通知回调的领券处理发生错误...";
			logger.error("商家券通知回调的领取事件处理出错了。。。{}",ex);
			ret = false;
			dbUtil.conn.rollback();

		} finally {
			dbUtil.closeNoErr();
		}

		logger.info("{}",log);
		//不管数据库有没有，应该记录这个事件，可能会有用，因为进入这里面的代码，已经领券成功了。
		log = log + notifyRes.toJSONString().replaceAll("[{}]","");//过滤{}尖
		adminLogService.write(LogEnum.COUPON_FETCH.name,LogEnum.COUPON_FETCH.index,log);

		//返回
		return ret;
	}


}
